using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using NUnit.Framework;
using NntpClientLib;

namespace NntpClientLibUnitTests
{
    [TestFixture]
    public class Rfc977NntpClientTestFixture
    {
        private string Server
        {
            get { return ConfigurationManager.AppSettings["FreeNntpHostname"]; }
        }

        private string TestNewsGroup
        {
            get { return ConfigurationManager.AppSettings["FreeTestNewsgroup"]; }
        }

        internal class ArticleProcessor : IArticleHeadersProcessor, IArticleBodyProcessor
        {
            private int _headers;
            private int _lines;

            public void AddText(string line)
            {
                _lines++;
            }

            public void AddHeader(string headerAndValue)
            {
                _headers++;
            }

            public void AddHeader(string header, string value)
            {
                _headers++;
            }

            public bool IsValid()
            {
                return _headers > 0 && _lines > 0;
            }
        }

        [Test]
        public void CheckIfHelpIsImplemented()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                foreach (var s in client.RetrieveHelp())
                {
                    Console.WriteLine(s);
                }
            }
        }

        [Test]
        public void ConnectionDefault()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                client.Close();
            }
        }

        [Test]
        public void ConnectionDefaultWithLogging()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.ProtocolLogger = Console.Error;
                client.Connect(Server);
                foreach (var s in client.RetrieveHelp())
                {
                }

                client.ProtocolLogger = null;
                foreach (var s in client.RetrieveHelp())
                {
                }
            }
        }

        [Test]
        public void GetAllHeadersForArticleRange()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                client.SelectNewsgroup(TestNewsGroup);
                var count = 0;
                foreach (var h in client.RetrieveArticleHeaders(client.CurrentGroup.LastArticleId - 20, client.CurrentGroup.LastArticleId))
                {
                    if (count++ > 10)
                    {
                        break;
                    }
                }
                Assert.LessOrEqual(10, count, "Not necessarily an error. Assumes newsgroup contains at least 10 articles.");
            }
        }

        [Test]
        public void GetAllHeadersForArticleRange423()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.ProtocolLogger = Console.Out;
                client.Connect(Server);
                client.SelectNewsgroup(TestNewsGroup);
                
                var count = 0;
                foreach (var h in client.RetrieveArticleHeaders(client.CurrentGroup.LastArticleId - 10, client.CurrentGroup.LastArticleId))
                {
                    if (count++ > 10)
                    {
                        break;
                    }
                }
                Assert.LessOrEqual(10, count, "Not necessarily an error. Assumes newsgroup contains at least 10 articles.");
            }
        }

        [Test]
        public void GetAllHeadersForArticleRangeInLoop()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                client.SelectNewsgroup(TestNewsGroup);

                for (var i = client.CurrentGroup.LastArticleId - 20; i < client.CurrentGroup.LastArticleId; i++)
                {
                    try
                    {
                        client.RetrieveArticleHeader(i);
                    }
                    catch (NntpResponseException error)
                    {
                        if (error.LastResponseCode == 423)
                        {
                            continue;
                        }
                    }
                }
            }
        }

        [Test]
        public void GetMutlipleArticlesBodies()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.ProtocolLogger = Console.Error;
                client.Connect(Server);
                client.SelectNewsgroup(TestNewsGroup);

                var d = client.RetrieveArticleHeader();
                
                Assert.Greater(client.CurrentGroup.EstimatedCount, 5, "Not an error. This test assumes 5 available articles.");
                for (var i = 0; i < 5; i++)
                {
                    foreach (var b in client.RetrieveArticleBody())
                    {
                    }
                }
            }
        }

        [Test]
        public void GetNewGroupsDateBoundaries()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                var now = DateTime.Now;
                var target = new DateTime(now.Year, now.Month, 1);

                foreach (var h in client.RetrieveNewNewsgroups(target.AddMonths(-1)))
                {
                }

                // Now, switch the day to an invalid month number, this tests to make sure the
                // date reformating in the library has the correct order (27 not a valid month).
                target = new DateTime(now.Year, now.Month, 27);

                foreach (var h in client.RetrieveNewNewsgroups(target.AddMonths(-1)))
                {
                }
            }
        }

        [Test]
        public void GetNewGroupsSimple()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                foreach (var h in client.RetrieveNewNewsgroups(DateTime.Now.AddMonths(-1)))
                {
                }
            }
        }

        [Test]
        [Explicit]
        public void GetNewNews()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                foreach (var s in client.RetrieveNewNews("comp.sys.mac.*", DateTime.Now.AddDays(-1.0)))
                {
                }
            }
        }

        [Test]
        public void GetNewNewsCatchServerError()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                client.ProtocolLogger = Console.Error;
                try
                {
                    foreach (var s in client.RetrieveNewNews("comp.sys.mac.*", DateTime.Now.AddDays(-1.0)))
                    {
                    }
                }
                catch (NntpResponseException exception)
                {
                    if (exception.LastResponseCode == 502 || exception.LastResponseCode == 480)
                        return;
                    Assert.Fail("Expected a error " + exception.LastResponseCode);
                }
            }
        }

        [Test]
        public void GetNewsGroups()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                var groupCount = 0;
                var groups = client.RetrieveNewsgroups().ToArray();
                foreach (var h in groups)
                {
                    groupCount++;
                }
                Assert.Greater(groupCount, 100);

                File.WriteAllLines("/temp/groups.txt", groups.Select(r => r.ToString()));
            }
        }

        [Test]
        public void GetNewsGroupsWithFilter()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                var groupCount = 0;
                foreach (var h in client.RetrieveNewsgroups())
                {
                    if (h.GroupName.StartsWith("comp.sys.mac"))
                    {
                        groupCount++;
                    }
                }

                Assert.Greater(groupCount, 10);
                Assert.Less(groupCount, 200);
            }
        }

        [Test]
        public void GetSingleArticleBody()
        {
            using (var client = new Rfc977NntpClientWithExtensions())
            {
                client.ProtocolLogger = Console.Error;
                client.Connect(Server);
                client.SelectNewsgroup(TestNewsGroup);

                var d = client.RetrieveArticleHeader();
                foreach (var b in client.RetrieveArticleBody())
                {
                }
            }
        }

        [Test]
        public void GetSingleArticleBodyWithProcessors()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                client.SelectNewsgroup(TestNewsGroup);

                var d = client.RetrieveArticleHeader();
                var ap = new ArticleProcessor();
                client.RetrieveArticle(ap, ap);
                Assert.IsTrue(ap.IsValid(), "Didn't read header line or body text lines.");
            }
        }

        [Test]
        public void GroupSelect()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                client.SelectNewsgroup(TestNewsGroup);
            }
        }

        [Test]
        public void MoveArticleBackwardFromEnd()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                client.SelectNewsgroup(TestNewsGroup);
                client.RetrieveStatistics(client.CurrentGroup.LastArticleId);

                var range = 3;

                Assert.Greater(client.CurrentGroup.EstimatedCount, range, "Article count may not be large enough, this might not be an error.");
                var count = range;

                while (--count > 0)
                {
                    var ids = client.SetPreviousArticle();
                }
                Assert.AreEqual(0, count, "Should have backed up to 20 articles.");
            }
        }

        [Test]
        public void MoveArticleForwardAndBackward()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                client.ProtocolLogger = Console.Error;
                client.SelectNewsgroup(TestNewsGroup);

                var range = 3;

                Assert.Greater(client.CurrentGroup.EstimatedCount, range, "Article count may not be large enough, this might not be an error.");
                var count = 0;

                for (; count < range; count++)
                {
                    var ids = client.SetNextArticle();
                }

                for (; count > 0; count--)
                {
                    var ids = client.SetPreviousArticle();
                }
                Assert.AreEqual(0, count, "Should have backed up same count as forward.");
            }
        }

        [Test]
        public void MoveArticleForwardFromBeginning()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                client.SelectNewsgroup(TestNewsGroup);

                var range = 3;

                Assert.Greater(client.CurrentGroup.EstimatedCount, range, "Article count may not be large enough, this might not be an error.");
                var count = 0;

                for (; count < range; count++)
                {
                    var ids = client.SetNextArticle();
                }
                Assert.Greater(count, 2, "Could not get more than 2 LAST commands, this might not be an error.");
            }
        }

        [Test]
        [Explicit]
        [ExpectedException(typeof (NntpResponseException))]
        public void PostArticle()
        {
            using (var client = new Rfc977NntpClient())
            {
                client.Connect(Server);
                client.SelectNewsgroup(TestNewsGroup);

                var headers = new ArticleHeadersDictionary();
                headers.AddHeader("From", "anoymous@anon.com (Anonymous)");
                headers.AddHeader("Subject", "Test - Please Ignore.");
                headers.AddHeader("Newsgroups", TestNewsGroup);
                headers.AddHeader("Date", new NntpDateTime(DateTime.Now).ToString());

                var body = new List<string>();
                body.Add("One line test message body.");

                client.PostArticle(new ArticleHeadersDictionaryEnumerator(headers), body);
            }
        }
    }
}